package com.ljc.db.backend.dm.page;

import com.ljc.db.backend.dm.pageCache.PageCache;
import com.ljc.db.backend.utils.Parser;

import java.util.Arrays;

/**
 * PageX管理普通页
 * 普通页结构【页首空闲空间偏移量+数据。。。+数据】
 * [FreeSpaceOffset] [Data]
 * FreeSpaceOffset: 2字节 空闲位置开始偏移
 */
public class PageX {
    
    private static final short OF_FREE = 0; // FreeSpaceOffset的起始位置
    private static final short OF_DATA = 2; // FreeSpaceOffset所占大小
    // 最大剩余空间 = 页大小 - 当前存放了数据的偏移量
    public static final int MAX_FREE_SPACE = PageCache.PAGE_SIZE - OF_DATA;

    public static byte[] initRaw() {
        byte[] raw = new byte[PageCache.PAGE_SIZE];
        setFSO(raw, OF_DATA); // 初始化时将OF_DATA = 2写到页首，表明数据都要从偏移量2开始向后插入
        return raw;
    }

    // 将新的空闲空间偏移量覆盖到普通页的页首
    private static void setFSO(byte[] raw, short ofData) {
        System.arraycopy(Parser.short2Byte(ofData), 0, raw, OF_FREE, OF_DATA);
    }

    // 获取pg的FSO【空闲空间偏移量】
    public static short getFSO(Page pg) {
        return getFSO(pg.getData());
    }

    // 获取空闲空间偏移量
    private static short getFSO(byte[] raw) {
        // 获得整个page的页首两个位置的数据【即FreeSpaceOffset的值】
        return Parser.parseShort(Arrays.copyOfRange(raw, 0, 2));
    }

    // 将raw插入pg中，返回插入位置
    public static short insert(Page pg, byte[] raw) {
        // 向页面写入数据，将该页面设置为脏页面
        pg.setDirty(true);
        // 获取空闲空间的偏移量
        short offset = getFSO(pg.getData());
        // 将raw中的数据复制到页中对应位置
        System.arraycopy(raw, 0, pg.getData(), offset, raw.length);
        // 更新空闲空间偏移量
        setFSO(pg.getData(), (short)(offset + raw.length));
        return offset;
    }

    // 获取页面的空闲空间大小
    public static int getFreeSpace(Page pg) {
        return PageCache.PAGE_SIZE - (int)getFSO(pg.getData());
    }

    // --------- 下面两个方法用于 数据库崩溃后重新打开数据库 恢复数据【逻辑和上面的方法类似】 ---------------->
    // 将raw插入pg中的offset位置，并将pg的offset设置为较大的offset
    public static void recoverInsert(Page pg, byte[] raw, short offset) {
        pg.setDirty(true);
        System.arraycopy(raw, 0, pg.getData(), offset, raw.length);

        short rawFSO = getFSO(pg.getData());
        if(rawFSO < offset + raw.length) {
            setFSO(pg.getData(), (short)(offset + raw.length));
        }
    }

    // 将raw插入pg中的offset位置，不更新update
    public static void recoverUpdate(Page pg, byte[] raw, short offset) {
        pg.setDirty(true);
        System.arraycopy(raw, 0, pg.getData(), offset, raw.length);
    }
}
